home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Alles Voor Internet / Tout Pour Internet
/
alles voor internet.iso
/
MacInternet™
/
Telnet
/
NCSA
/
tn3270 2.4d7 source
/
tn3270
/
telnet.c
< prev
next >
Wrap
Text File
|
1992-04-17
|
22KB
|
844 lines
/*
* tn3270 for the Macintosh Source Code
* Brown University Computing and Information Services
* Version 2.4d7 April, 1992
* Copyright (c) 1988, 1989, 1990, 1991, 1992 by Brown University and by
* Peter John DiCamillo.
*
* Permission is granted to any individual or institution to use, copy,
* or redistribute the binary version of this software and its
* documentation provided this notice and the copyright notices are
* retained. Permission is granted to any individual or non-profit
* institution to use, copy, modify, or redistribute the source files
* of this software provided this notice and the copyright notices are
* retained. This software may not be distributed for profit, either
* in original form or in derivative works, nor can the source be
* distributed to other than an individual or a non-profit institution.
* Any individual or group interested in seeing and/or using these
* source files but who are prevented from doing so by the above
* constraints should contact Don Wolfe, Assistant Vice-President for
* Computer Systems at Brown University, (401) 863-7250, for possible
* software licensing of the source developed at Brown.
*
* Brown University and Peter John DiCamillo make no representations
* about the suitability of this software for any purpose.
*
* BROWN UNIVERSITY AND PETER JOHN DICAMILLO GIVE NO WARRANTY, EITHER
* EXPRESS OR IMPLIED, FOR THE PROGRAM AND/OR DOCUMENTATION PROVIDED,
* INCLUDING, WITHOUT LIMITATION, WARRANTY OF MERCHANTABILITY AND
* WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE.
* -----------------------------------------------------------------------
* The following copyright notice applies to the code in this file for
* interpreting the Telnet protocol and performing option negotiations:
* -----------------------------------------------------------------------
* Copyright (c) 1984-1987 by the Regents of the
* University of California and by Gregory Glenn Minshall.
*
* Permission to use, copy, modify, and distribute these
* programs and their documentation for any purpose and
* without fee is hereby granted, provided that this
* copyright and permission appear on all copies and
* supporting documentation, the name of the Regents of
* the University of California not be used in advertising
* or publicity pertaining to distribution of the programs
* without specific prior permission, and notice be given in
* supporting documentation that copying and distribution is
* by permission of the Regents of the University of California
* and by Gregory Glenn Minshall. Neither the Regents of the
* University of California nor Gregory Glenn Minshall make
* representations about the suitability of this software
* for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#if !defined(USEDUMP)
#include "maclib.h"
#include "termdef.h"
#include "tn3270funcs.h"
#include "globals.h"
#else
#pragma load "tn3270DumpFile"
#endif
#define TELCMDS 1
#define TELOPTS 1
#include "telnet.h"
#pragma segment 3270tcp
/*
* Telnet receiver states for fsm
*/
#define TS_DATA 0
#define TS_IAC 1
#define TS_WILL 2
#define TS_WONT 3
#define TS_DO 4
#define TS_DONT 5
/* #define TS_CR 6 currently not used */
#define TS_SB 7 /* sub-option collection */
#define TS_SE 8 /* looking for sub-option end */
static unsigned char doopt[] = { IAC, DO, '%', 'c', 0 };
static unsigned char dont[] = { IAC, DONT, '%', 'c', 0 };
static unsigned char will[] = { IAC, WILL, '%', 'c', 0 };
static unsigned char wont[] = { IAC, WONT, '%', 'c', 0 };
#define SB_CLEAR() cp->subpointer = cp->subbuffer;
#define SB_TERM() cp->subend = cp->subpointer;
#define SB_ACCUM(c) if (cp->subpointer < (cp->subbuffer+sizeof(cp->subbuffer))) { \
*(cp->subpointer)++ = (c); \
}
static unsigned char sb_terminal[] = { IAC, SB,
TELOPT_TTYPE, TELQUAL_IS,
'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
IAC, SE };
static unsigned char sb_e_terminal[] = { IAC, SB,
TELOPT_TTYPE, TELQUAL_IS,
'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
'-', 'E',
IAC, SE };
#define SBTERMTYPE 11
#define SBTERMMODEL 13
void telrcv(register unsigned char *sbp,
register short scc,
cnr *cp)
{
register unsigned char c;
register unsigned char *Sbp;
register short Scc;
short endflg, skipcnt;
if (scc <= 0) return;
if (cf_dblevel > 2) dbgdump("telrcv", sbp, scc);
cp->replyptr = cp->replybuff;
while (scc > 0) {
/* if (cf_dblevel > 2) {
sprintf(dbgmsg, "scc = %d", scc);
putln(dbgmsg);
} */
c = *sbp++;
scc--;
switch (cp->telrcv_state) {
case TS_DATA:
if (c == IAC) {
cp->telrcv_state = TS_IAC;
continue;
}
Sbp = sbp;
Scc = scc;
skipcnt = 0;
while (Scc > 0) {
c = *Sbp++;
Scc--;
if (c == IAC) {
skipcnt++;
cp->telrcv_state = TS_IAC;
break;
}
}
endflg = 0;
if ((cp->telrcv_state == TS_IAC) && (Scc > 0))
if ((*Sbp) == EOR) {
skipcnt++;
endflg = 1;
Sbp++;
Scc--;
cp->telrcv_state = TS_DATA;
}
tcpdata(sbp-1, scc+1-Scc-skipcnt, cp->data_init, endflg, cp);
cp->data_init = endflg;
sbp = Sbp;
scc = Scc;
break;
case TS_IAC:
switch (c) {
case WILL:
cp->telrcv_state = TS_WILL;
continue;
case WONT:
cp->telrcv_state = TS_WONT;
continue;
case DO:
cp->telrcv_state = TS_DO;
continue;
case DONT:
cp->telrcv_state = TS_DONT;
continue;
case DM:
/*
* We may have missed an urgent notification,
* so make sure we flush whatever is in the
* buffer currently.
*/
/* SYNCHing = 1;
ttyflush();
SYNCHing = stilloob(net);
settimer(gotDM); */
break;
case NOP:
case GA:
break;
case SB:
SB_CLEAR();
cp->telrcv_state = TS_SB;
continue;
case EOR:
tcpdata(0L, 0, cp->data_init, 1, cp);
cp->data_init = 1;
break;
case IAC:
tcpdata(sbp-1, 1, cp->data_init, 0, cp);
cp->data_init = 0;
break;
default:
break;
}
cp->telrcv_state = TS_DATA;
continue;
case TS_WILL:
printoption(">RCVD", will, c, !(cp->hisopts)[c]);
/* if (c == TELOPT_TM) {
if (flushout) {
flushout = 0;
}
}
else */ if (!(cp->hisopts)[c]) {
willoption(c, 1, cp);
}
SetIn3270(cp);
cp->telrcv_state = TS_DATA;
continue;
case TS_WONT:
printoption(">RCVD", wont, c, (cp->hisopts)[c]);
/* if (c == TELOPT_TM) {
if (flushout) {
flushout = 0;
}
}
else */ if ((cp->hisopts)[c]) {
wontoption(c, 1, cp);
}
SetIn3270(cp);
cp->telrcv_state = TS_DATA;
continue;
case TS_DO:
printoption(">RCVD", doopt, c, !(cp->myopts)[c]);
if (!(cp->myopts)[c])
dooption(c, cp);
SetIn3270(cp);
cp->telrcv_state = TS_DATA;
continue;
case TS_DONT:
printoption(">RCVD", dont, c, (cp->myopts)[c]);
if ((cp->myopts)[c]) {
(cp->myopts)[c] = 0;
sprintf(cp->replyptr, wont, c);
cp->replyptr += sizeof (wont) - 2;
/* flushline = 1; */
/* setconnmode(); set new tty mode (maybe) */
printoption(">SENT", wont, c, 0);
}
SetIn3270(cp);
cp->telrcv_state = TS_DATA;
continue;
case TS_SB:
if (c == IAC) {
cp->telrcv_state = TS_SE;
}
else {
SB_ACCUM(c);
}
continue;
case TS_SE:
if (c != SE) {
if (c != IAC) {
SB_ACCUM(IAC);
}
SB_ACCUM(c);
cp->telrcv_state = TS_SB;
}
else {
SB_TERM();
suboption(cp); /* handle sub-option */
SetIn3270(cp);
cp->telrcv_state = TS_DATA;
}
continue;
default:
break;
}
}
if (cp->quitflg) {
cp->quitflg = 0;
return;
}
if (cp->replyptr != cp->replybuff) {
tcpwrite(cp->replybuff, cp->replyptr-cp->replybuff, cp);
/* sends data, or ends connection if unable to send */
}
}
/*VARARGS*/
void printoption(unsigned char *direction, unsigned char *fmt,
short option, short what)
{
if (!cf_dblevel) return;
sprintf(dbgmsg, "%s ", direction+1);
putln(dbgmsg);
if (fmt == doopt)
fmt = "do";
else if (fmt == dont)
fmt = "dont";
else if (fmt == will)
fmt = "will";
else if (fmt == wont)
fmt = "wont";
else
fmt = "???";
if (option < (sizeof(telopts)/sizeof( telopts[0])))
sprintf(dbgmsg, "%s %s", fmt, telopts[option]);
else
sprintf(dbgmsg, "%s %d", fmt, option);
putln(dbgmsg);
if (*direction == '<') {
putln("\n");
return;
}
sprintf(dbgmsg, " (%s)\n", what ? "reply" : "don't reply");
putln(dbgmsg);
}
void willoption(short option, short reply, cnr *cp)
{
unsigned char *fmt;
switch (option) {
case TELOPT_ECHO:
/*
* The following is a pain in the rear-end.
* Various IBM servers (some versions of Wiscnet,
* possibly Fibronics/Spartacus, and who knows who
* else) will NOT allow us to send "DO SGA" too early
* in the setup proceedings. On the other hand,
* 4.2 servers (telnetd) won't set SGA correctly.
* So, we are stuck. Empirically (but, based on
* a VERY small sample), the IBM servers don't send
* out anything about ECHO, so we postpone our sending
* "DO SGA" until we see "WILL ECHO" (which 4.2 servers
* DO send).
*/
{
if (cp->askedSGA == 0) {
cp->askedSGA = 1;
if (!(cp->hisopts)[TELOPT_SGA]) {
willoption(TELOPT_SGA, 0, cp);
}
}
}
/* Fall through */
case TELOPT_EOR:
case TELOPT_BINARY:
case TELOPT_SGA:
case TELOPT_3270:
/* settimer(modenegotiated); */
(cp->hisopts)[option] = 1;
fmt = doopt;
/* setconnmode(); possibly set new tty mode */
break;
case TELOPT_TM:
return; /* Never reply to TM will's/wont's */
default:
fmt = dont;
break;
}
sprintf(cp->replyptr, fmt, option);
cp->replyptr += sizeof (dont) - 2;
if (reply)
printoption(">SENT", fmt, option, reply);
else
printoption("<SENT", fmt, option, reply);
}
void wontoption(short option, short reply, cnr *cp)
{
unsigned char *fmt;
switch (option) {
case TELOPT_BINARY: /* added for Mac tn3270 */
case TELOPT_ECHO:
case TELOPT_SGA:
case TELOPT_3270:
/* settimer(modenegotiated); */
(cp->hisopts)[option] = 0;
fmt = dont;
/* setconnmode(); Set new tty mode */
break;
case TELOPT_TM:
return; /* Never reply to TM will's/wont's */
default:
fmt = dont;
}
sprintf(cp->replyptr, fmt, option);
cp->replyptr += sizeof (doopt) - 2;
if (reply)
printoption(">SENT", fmt, option, reply);
else
printoption("<SENT", fmt, option, reply);
}
void dooption(short option, cnr *cp)
{
unsigned char *fmt;
switch (option) {
case TELOPT_TM:
fmt = will;
break;
case TELOPT_EOR:
case TELOPT_BINARY:
case TELOPT_TTYPE: /* terminal type option */
case TELOPT_SGA: /* no big deal */
case TELOPT_3270: /* what we're here for */
fmt = will;
(cp->myopts)[option] = 1;
break;
case TELOPT_ECHO: /* We're never going to echo... */
default:
fmt = wont;
break;
}
sprintf(cp->replyptr, fmt, option);
cp->replyptr += sizeof (doopt) - 2;
printoption(">SENT", fmt, option, 0);
}
/*
* suboption(cnr *cp)
*
* Look at the sub-option buffer, and try to be helpful to the other
* side.
*
* Currently we recognize:
*
* Terminal type, send request.
*/
void suboption(cnr *cp)
{
unsigned char * usertype;
short usersize;
printsub("<", cp->subbuffer, cp->subend-cp->subbuffer+1);
switch (cp->subbuffer[0]&0xff) {
case TELOPT_TTYPE:
if ((cp->subbuffer[1]&0xff) == TELQUAL_SEND) {
/* check for a specific type the user specified after
the host name. */
usertype = cp->connhostname;
while ((*usertype != '\0') && (*usertype != ':')) usertype++;
if (*usertype == ':') {
usertype++;
usersize = 0;
while ((usertype[usersize] != '\0') &&
(usertype[usersize] != ':')) usersize++;
if (usersize > 0) {
memcpy(cp->replyptr, sb_terminal, 4);
memcpy(cp->replyptr+4, usertype, usersize);
memcpy(cp->replyptr+4+usersize, sb_terminal+14, 2);
printsub(">", cp->replyptr+2, 4+usersize);
cp->replyptr += usersize + 6;
if (strncmp(usertype, "IBM-327", 7) == 0)
cp->sent3270tt = 1;
return;
}
}
/*
* Try to send a 3270 type terminal name. Decide which one based
* on the format of our screen, color capabilities, whether
* we should support extended data streams, and whether we are
* sending the first or a subsequent terminal name.
*/
/* Use 3278 unless color is available.
With color, use 3279 unless extended datastream support
is not wanted (cp->cs.ext3270 = 0) and we already tried sending
3279 previously (cp->sent3270tt = 1) */
if (colormac && (!cp->cs.nocolor) && !((!cp->cs.ext3270)
&& cp->sent3270tt)) {
sb_terminal[SBTERMTYPE] = '9';
sb_e_terminal[SBTERMTYPE] = '9';
}
else {
sb_terminal[SBTERMTYPE] = '8';
sb_e_terminal[SBTERMTYPE] = '8';
}
/* set model number */
sb_terminal[SBTERMMODEL] =
getmodel(cp->cs.altrows, cp->cs.altcols, cp) & 0x0f;
sb_terminal[SBTERMMODEL] += 0x30;
sb_e_terminal[SBTERMMODEL] = sb_terminal[SBTERMMODEL];
/* For model 4 and 5, always use 3278, since there are no
real 3279-4 and 3279-5 terminals. The VM logical device
facility rejects 3279-4 and 3279-5, even though VM TCP/IP
accepts them (yes, it's a bug). */
if ((sb_terminal[SBTERMMODEL] == '4') ||
(sb_terminal[SBTERMMODEL] == '5')) {
sb_e_terminal[SBTERMTYPE] = sb_terminal[SBTERMTYPE] ='8';
}
/* Append "-E" to terminal type unless extended datastream
support is not wanted (cp->cs.ext3270 = 0) or a previous
terminal type ending with "-E" was possibly rejected
(cp->sent3270tt = 1). */
if (cp->cs.ext3270 && (!cp->sent3270tt)) {
memcpy(cp->replyptr, sb_e_terminal, sizeof(sb_e_terminal));
printsub(">", cp->replyptr+2, sizeof(sb_e_terminal)-2);
cp->replyptr += sizeof(sb_e_terminal);
}
else {
memcpy(cp->replyptr, sb_terminal, sizeof(sb_terminal));
printsub(">", cp->replyptr+2, sizeof(sb_terminal)-2);
cp->replyptr += sizeof(sb_terminal);
}
cp->sent3270tt = 1;
return;
}
default:
break;
}
}
void SetIn3270(cnr *cp)
{
char do3270;
cp->data_init = 1; /* reset data flag after negotiation */
if (!(cp->newserver)) {
cp->newserver = ((cp->myopts)[TELOPT_3270] != 0) ||
((cp->hisopts)[TELOPT_3270] != 0);
}
if (cp->newserver) {
do3270 = ((cp->myopts)[TELOPT_3270] /* && (cp->hisopts)[TELOPT_3270] */);
}
else {
do3270 = (cp->sent3270tt && (cp->myopts)[TELOPT_BINARY]
&& (cp->hisopts)[TELOPT_BINARY] /* && !donebinarytoggle */);
}
if (do3270) {
if (!(cp->logon)) {
cp->showfkey = 1; /* enable fkey menu */
appl_menu();
cp->connstate = 5;
cp->logon = 1;
newstat(cp);
}
}
else {
if (cp->logon) {
cp->hadascii = 1;
cp->connstate = 4;
cp->logon = 0;
cp->skiplf = cp->escmode = 0;
cp->showfkey = 0; /* disable fkey menu */
appl_menu();
cp->kblock = cp->aplmode = cp->insmode = 0;
cp->fixbracket = cp->cs.std_brack && (!(cp->aplmode)) &&
(cp->stdfont != ALAFONT);
cp->kblcode = 0;
cp->fsinv = 0;
newstat(cp);
clrscn(cp);
invldscr(cp);
}
}
}
void printsub(unsigned char *direction, /* "<" or ">" */
unsigned char *pointer, /* where suboption data sits */
short length) /* length of suboption data */
{
if (cf_dblevel) {
sprintf(dbgmsg, "%s suboption ",
(direction[0] == '<')? "Received":"Sent");
putln(dbgmsg);
switch (pointer[0]) {
case TELOPT_TTYPE:
sprintf(dbgmsg, "Terminal type ");
putln(dbgmsg);
switch (pointer[1]) {
case TELQUAL_IS:
{
unsigned char tmpbuf[/*sizeof(cp->subbuffer)*/ 100];
short minlen;
if (length > 3) length -= 3;
if (length < sizeof(tmpbuf)) minlen = length;
else minlen = sizeof(tmpbuf);
memcpy(tmpbuf, pointer+2, minlen);
tmpbuf[minlen-1] = 0;
sprintf(dbgmsg, "is %s.\n", tmpbuf);
putln(dbgmsg);
}
break;
case TELQUAL_SEND:
sprintf(dbgmsg, "- request to send.\n");
putln(dbgmsg);
break;
default:
sprintf(dbgmsg,
"- unknown qualifier %d (0x%x).\n", pointer[1]);
putln(dbgmsg);
}
break;
default:
sprintf(dbgmsg, "Unknown option %d (0x%x)\n",
pointer[0], pointer[0]);
putln(dbgmsg);
}
}
}
void tcpdata(unsigned char *sptr, short slen,
char init, char end, cnr *cp)
{
short wsflen;
unsigned char * dataptr;
short datalen;
char winit;
if (cf_dblevel > 2) {
sprintf(dbgmsg, "tcpdata: len = %d, init = %d, end = %d",
slen, init, end);
putln(dbgmsg);
}
if (cp->apiopenpend) {
apiregister(1, cp); /* register session */
if (!(cp->logon)) {
apiopenresp(openLineMode, cp);
}
else {
apiopenresp(open3270Mode, cp);
}
}
if (!(cp->logon)) {
if (cp->kblock) {
cp->kblock = 0;
newstat(cp);
}
cp->hadascii = 1;
if (cp->servermode) putsrv(sptr, slen, 0, cp);
else putscr(sptr, slen, 0, cp);
writeresponse(0, cp);
return;
}
/* length 0 valid only if init==0 and end==1 */
if ((slen == 0) && (init || (!end))) return;
if (init) {
cp->wsfflag = ((sptr[0] == 0x11) || (sptr[0] == 0xf3));
if (cp->wsfflag) {
cp->wsfptr = cp->wsfbuff;
cp->wsfleft = 8192;
cp->wsfop = sptr[0];
}
}
if (cp->wsfflag) {
if (slen > 0) {
if (slen > cp->wsfleft) wsflen = cp->wsfleft;
else wsflen = slen;
memcpy(cp->wsfptr, sptr, wsflen);
cp->wsfleft -= wsflen;
cp->wsfptr += wsflen;
}
if (end) {
/* invldscr(); */
if (cp->cs.dblevel > 1) dbgdump("writesf", cp->wsfbuff,
cp->wsfptr-cp->wsfbuff);
writesf(cp->wsfbuff, cp->wsfptr-cp->wsfbuff, 1, cp);
writeresponse(cp->wsfop, cp);
}
return;
}
if (init) {
cp->lstopcd = sptr[0];
cp->xtralen = cp->xtrainit = 0;
cp->needtoend = 0;
}
switch (cp->lstopcd) {
case 0x01: /* write */
case 0x05: /* erase/write */
case 0x0d: /* erase/write alt. */
case 0xf1: /* SNA write */
case 0xf5: /* SNA erase/write */
case 0x7e: /* SNA erase/write alt. */
if (init && (slen == 1)) { /* just op-code */
cp->xtralen = 1; /* save op-code until wcc */
cp->xtradata[0] = sptr[0];
cp->xtrainit = 1;
break;
}
if (slen > 0) {
cp->needtoend = 1;
if (cp->xtralen > 0)
memcpy(sptr-cp->xtralen, cp->xtradata, cp->xtralen);
cp->ldvoff = 0;
dataptr = sptr-cp->xtralen;
datalen = slen + cp->xtralen;
if (cp->xtrainit) winit = 1;
else winit = init;
if (cp->cs.dblevel > 1) {
sprintf(dbgmsg, "writetm: init = %d", winit);
dbgdump(dbgmsg, dataptr, datalen);
}
writetm(dataptr, datalen, winit, cp);
cp->xtralen = datalen - cp->ldvoff;
cp->xtrainit = 0;
if (cp->xtralen == 0) break;
if (cp->xtralen > 64) cp->xtralen = 64;
memcpy(cp->xtradata, dataptr+cp->ldvoff, cp->xtralen);
}
break;
case 0x0f: /* erase all unprot. */
case 0x6f: /* SNA erase all unprot. */
if (!init) return;
funckey(10, 0, cp);
cp->rdaid = 60;
writeresponse(cp->lstopcd, cp);
return;
case 0x06: /* read modified */
case 0xf6: /* SNA read modified */
if (!init) return;
rdmcmd(cp->rdaid, cp);
writeresponse(cp->lstopcd, cp);
return;
case 0x6e: /* SNA read modified all */
if (!init) return;
rdmacmd(cp->rdaid, cp);
writeresponse(cp->lstopcd, cp);
return;
case 0x02: /* read buffer */
case 0xf2: /* SNA read buffer */
if (!init) return;
rdbuff(cp);
writeresponse(cp->lstopcd, cp);
return;
default:
return;
}
if (end) {
if (cp->needtoend) {
endwcc(cp); /* sets pndbeep if beep needed */
writeresponse(cp->lstopcd, cp);
cp->needtoend = 0;
}
cp->fsinv = 1; /* tcpevent will call invldscr and possibly beep */
}
}
void tcpkbin(unsigned char asc, unsigned char chr,
unsigned char shift, cnr *cp)
{ /* handle new character in line mode */
#pragma unused(shift)
static unsigned char nl[2] = {0x0d, 0x0a};
static unsigned char brk[2] = {0xff, 0xf3};
static unsigned char del[2] = {0xff, 0xf7};
static unsigned char up[2] = {0x1b, 0x41};
static unsigned char down[2] = {0x1b, 0x42};
static unsigned char right[2] = {0x1b, 0x43};
static unsigned char left[2] = {0x1b, 0x44};
static unsigned char pf1[3] = {0x1b, 0x4f, 0x50};
static unsigned char pf2[3] = {0x1b, 0x4f, 0x51};
static unsigned char pf3[3] = {0x1b, 0x4f, 0x52};
static unsigned char pf4[3] = {0x1b, 0x4f, 0x53};
unsigned char c;
switch(chr) { /* Note: keyboard event processing code must be
updated if this list of functions is changed. */
case 190: /* "rub-out" function */
tcpwrite(del, 2, cp);
break;
case 170: /* "clear" function */
tcpwrite(brk, 2, cp);
break;
case 142: /* "enter" function */
tcpwrite(nl, 2, cp);
break;
case 175: /* "left" function */
c = 0x08; /* send normal backspace */
tcpwrite(&c, 1, cp);
break;
case 143: /* "pf1" function */
tcpwrite(pf1, 3, cp);
break;
case 144: /* "pf2" function */
tcpwrite(pf2, 3, cp);
break;
case 145: /* "pf3" function */
tcpwrite(pf3, 3, cp);
break;
case 146: /* "pf4" function */
tcpwrite(pf4, 3, cp);
break;
case 173: /* "up" function */
tcpwrite(up, 2, cp);
break;
case 174: /* "down" function */
tcpwrite(down, 2, cp);
break;
case 184: /* "delete-char" function */
tcpwrite(left, 2, cp);
break;
case 176: /* "right" function */
tcpwrite(right, 2, cp);
break;
default:
if (asc > 127) return;
c = asc;
tcpwrite(&c, 1, cp);
break;
}
}